/*
 * topological-hex, a program to compute combinatorial hexahedral meshes.
 *
 * Copyright (C) <2018> <Université catholique de Louvain (UCL), Belgique>
 *
 * List of the contributors to the development, description and complete
 * License: see LICENSE file.
 *
 * This program (topological-hex) is free software:
 * you can redistribute it and/or modify it under the terms
 * of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see COPYING file).  If not,
 * see <http://www.gnu.org/licenses/>.
 */

#ifndef TYPES_H_
#define TYPES_H_

#include <stdint.h>
#include <limits.h>

/**
 * @defgroup types Primitive Types
 *
 * Type definitions for integer types used to index or describe components of
 * the mesh, as well as basic operations to extract information from such
 * descriptors.
 */

/**
 * @addtogroup types
 * @{
 */

/**
 * Global index of a vertex within the mesh.
 */
typedef uint8_t vertex_index;

/**
 * Global index of an edge within the mesh.
 */
typedef uint8_t edge_index;

/**
 * Global index of a quad within the mesh.
 */
typedef uint8_t quad_index;

/**
 * Global index of a hexahedron within the mesh.
 */
typedef uint8_t hex_index;

/**
 * Local index of a vertex within a hexahedron.
 */
typedef uint8_t hex_vertex_index;

/**
 * Local index of an edge within a hexahedron.
 */
typedef uint8_t hex_edge_index;

/**
 * Local index of a quad within a hexahedron.
 */
typedef uint8_t hex_facet_index;

/**
 * Local index of a vertex within a quad.
 */
typedef uint8_t quad_vertex_index;

/**
 * Local index of an edge within a quad.
 */
typedef uint8_t quad_edge_index;

/**
 * Pair containing both the index of a hexahedron and the index of a facet
 * within that hexahedron.
 */
typedef uint16_t hex_facet;

/**
 * Pair containing both the index of a quad and the indexof an edge within that
 * quad.
 */
typedef uint16_t quad_edge;

/**
 * Partial description of a quadrilateral.
 *
 * This descriptor stores:
 *
 *    1. The index of the quadrilateral
 *    2. The index of a hexahedron which contains this quad.
 *    3. The index of the two vertices of one of the diagonal of this quad.
 *
 * If this descriptor is stored in an array at the index given by @ref
 * diagonal_index for the second diagonal of the quad, then all four vertices
 * can be determined.
 */
typedef uint32_t quad_descriptor;

/**
 * Special, invalid quad descriptor used to indicate empty entries.
 */
static const quad_descriptor EMPTY_QUAD_DESCRIPTOR = (quad_descriptor)0;

/**
 * Special index indicating the absence of a hexahedron (e.g. to indicate that
 * there is no other hexahedron on the other side of a facet).
 */
static const hex_index NO_HEXAHEDRON = (hex_index)-1;

/**
 * Special index used to represent the absence of a quadrilateral.
 */
static const quad_index NO_QUAD = (quad_index)-1;

/**
 * Special index used to represent the absence of an edge.
 */
static const edge_index NO_EDGE = (edge_index)-1;

/**
 * Attributes an index to an unordered pair (@p x, @p y).
 *
 * Both @p x and @p y must be less than @p n.
 */
static inline
uint32_t diagonal_index(uint32_t n, uint32_t a, uint32_t b) {
  return a < b ? n*a + b : n*b + a;
}

/**
 * Constructs a pair made up of the index of a hexahedron @p hex and of the
 * facet @p facet within this hexahedron.
 */
static inline
hex_facet make_hex_facet(hex_index hex, hex_facet_index facet) {
  return hex << 3 | facet;
}

/**
 * Given a @ref hex_facet, returns the index of the hexahedron this pair
 * references.
 */
static inline
hex_index hex_facet_hex(hex_facet obj) {
  return obj >> 3;
}

/**
 * Given a @ref hex_facet, returns the local index of the facet this pair
 * references.
 */
static inline
hex_facet_index hex_facet_facet(hex_facet obj) {
  return obj & ((1 << 3)-1);
}

static inline
quad_edge make_quad_edge(quad_index quad, quad_edge_index edge) {
  return ((quad_edge)quad  << 2) | edge;
}

static inline
quad_index quad_edge_quad(quad_edge edge) {
  return edge >> 2;
}

static inline
quad_edge_index quad_edge_edge(quad_edge edge) {
  return edge & ((1 << 2)-1);
}

static inline
quad_edge next_edge(quad_edge e) {
  return make_quad_edge(quad_edge_quad(e), (quad_edge_edge(e) + 1) % 4);
}

static inline
quad_edge opposite_edge(quad_edge e) {
  return make_quad_edge(quad_edge_quad(e), (quad_edge_edge(e) + 2) % 4);
}

static inline
quad_edge prev_edge(quad_edge e) {
  return make_quad_edge(quad_edge_quad(e), (quad_edge_edge(e) + 3) % 4);
}

/**
 * Constructs a partial description of a quadrilateral.
 *
 * @param quad Global index of the quad.
 * @param hex Global index of a hexahedron containing this quad.
 * @param va Index of one of the vertices of quad.
 * @param vb Index of the only vertex of quad which is not adjacent to @p va.
 */
static inline
quad_descriptor make_quad_descriptor(quad_index quad, hex_index hex,
                                     vertex_index va, vertex_index vb) {
  return quad | (uint32_t)hex << 8u |
    (uint32_t)va << 16u | (uint32_t)vb << 24u;
}

/**
 * Returns the index of the quad reference by this descriptor.
 */
static inline
quad_index quad_descriptor_quad_index(quad_descriptor desc) {
  return desc & UINT8_MAX;
}

/**
 * Returns the index of a hexahedron containing the quad reference by this
 * descriptor.
 */
static inline
hex_index quad_descriptor_hex_index(quad_descriptor desc) {
  return (desc >> 8) & UINT8_MAX;
}

/**
 * Returns one of the two elements of one of the diagonals of the quad
 * referenced by this descriptor.
 *
 * @see quad_descriptor_second_vertex
 */
static inline
vertex_index quad_descriptor_first_vertex(quad_descriptor desc) {
  return (desc >> 16) & UINT8_MAX;
}

/**
 * Returns one of the two elements of one of the diagonals of the quad
 * referenced by this descriptor.
 *
 *
 *
 * @see quad_descriptor_first_vertex
 */
static inline
vertex_index quad_descriptor_second_vertex(quad_descriptor desc) {
  return (desc >> 24) & UINT8_MAX;
}

/**
 * Enumeration used to indicate success or failure of a function. In the case of
 * failure, the specific value returned indicates the cause for this failure.
 */
typedef enum error_code {
  SUCCESS = 0,

  OUT_OF_MEMORY,
  IO_ERROR,
  PARSE_ERROR,
  SYSTEM_ERROR,
  PROTOCOL_ERROR,
  UNORIENTABLE_ERROR,
} error_code;

/**
 * Maps each vertex to the index of its 3 neighbors.
 */
static const hex_vertex_index HEX_NEIGHBORS[][3] = {
  {1, 3, 4},
  {2, 0, 5},
  {3, 1, 6},
  {0, 2, 7},
  {7, 5, 0},
  {4, 6, 1},
  {5, 7, 2},
  {6, 4, 3}
};

static const hex_vertex_index HEX_EDGES[][2] = {
  {0, 1},
  {0, 3},
  {0, 4},
  {1, 2},
  {1, 5},
  {2, 3},
  {2, 6},
  {3, 7},
  {4, 7},
  {4, 5},
  {5, 6},
  {6, 7},
};

/**
 * Maps each facet to the index of the opposite facet.
 */
static const hex_facet_index HEX_OPPOSITE_FACET[] = {5, 3, 4, 1, 2, 0};

/**
 * Maps each facet to its 4 vertices.
 *
 * The order used here is such that simultaneously iterating over the four
 * vertices of two opposite facets will yield pairs of opposite vertices.
 */
static const hex_vertex_index HEX_FACET_CONTENTS[][4] = {
  {0, 1, 2, 3},
  {4, 5, 1, 0},
  {5, 6, 2, 1},
  {7, 6, 2, 3},
  {4, 7, 3, 0},
  {4, 5, 6, 7}
};

/**
 * Maps each facet to its 4 vertices, using a consistent orientation for all
 * facets.
 */
static const hex_vertex_index HEX_ORIENTED_FACET_CONTENTS[][4] = {
  {0, 1, 2, 3},
  {4, 5, 1, 0},
  {5, 6, 2, 1},
  {7, 3, 2, 6},
  {4, 0, 3, 7},
  {4, 7, 6, 5}
};

/**
 * @}
 */

#endif
